package main

import (
	"errors"
	"math/rand"
	"time"
)

const Up = 1
const Right = 2
const Down = 3
const Left = 4
const NoChange = 0
const End = 5

const A = 97
const W = 119
const S = 115
const D = 100

const BoundaryStr = "*"
const Blank = " "
const CandyIcon = "@"
const SnakeBodyIcon = "="

var SnakeHeadIcons = [4]string{"A",">","v","<"}

type GSMap struct {
	MapLength int
	MapWidth int
	Canvas [][]string
}

type Coordinate struct {
	Ordinate int
	Abscissa int
}

type Vector struct {
	Coordinate
	Direction int
}

type Candy struct {
	Coordinate
}

type Snake struct {
	SnakeToward int
	SnakeLine []Vector
}

func (gsMap *GSMap) Init (length int,width int) error {
	if length<4 || width<4 {
		return errors.New("地图尺寸不合适")
	}
	gsMap.MapLength=length
	gsMap.MapWidth=width
	gsMap.Canvas=make([][]string,width)
	for i := 0; i < len(gsMap.Canvas); i++ {
		gsMap.Canvas[i]=make([]string,length)
		gsMap.Canvas[i][0]=BoundaryStr
		gsMap.Canvas[i][length-1]=BoundaryStr
	}
	for i:=0;i<len(gsMap.Canvas[0]);i++ {
		gsMap.Canvas[0][i]=BoundaryStr
	}
	for i:=0;i<len(gsMap.Canvas[width-1]);i++ {
		gsMap.Canvas[width-1][i]=BoundaryStr
	}
	ClearToBlank(gsMap)
	return nil
}

func (gsMap *GSMap) PaintSnakeAndCandyIntoMap (snake *Snake,candy *Candy) error {
	ClearToBlank(gsMap)
	gsMap.Canvas[candy.Ordinate][candy.Abscissa]=CandyIcon
	for i := 0; i < len(snake.SnakeLine)-1; i++ {
		if snake.SnakeLine[i].Direction==Up {
			j:=snake.SnakeLine[i].Abscissa
			for k:=snake.SnakeLine[i+1].Ordinate;k<snake.SnakeLine[i].Ordinate+1;k++ {
				gsMap.Canvas[k][j]=SnakeBodyIcon
			}
		}
		if snake.SnakeLine[i].Direction==Right {
			j:=snake.SnakeLine[i].Ordinate
			for k:=snake.SnakeLine[i].Abscissa;k<snake.SnakeLine[i+1].Abscissa+1;k++ {
				gsMap.Canvas[j][k]=SnakeBodyIcon
			}
		}
		if snake.SnakeLine[i].Direction==Down {
			j:=snake.SnakeLine[i].Abscissa
			for k:=snake.SnakeLine[i].Ordinate;k<snake.SnakeLine[i+1].Ordinate+1;k++ {
				gsMap.Canvas[k][j]=SnakeBodyIcon
			}
		}
		if snake.SnakeLine[i].Direction==Left {
			j:=snake.SnakeLine[i].Ordinate
			for k:=snake.SnakeLine[i+1].Abscissa;k<snake.SnakeLine[i].Abscissa+1;k++ {
				gsMap.Canvas[j][k]=SnakeBodyIcon
			}
		}
		if snake.SnakeLine[i].Direction==End {
			return errors.New("蛇末提前到来")
		}
	}
	if snake.SnakeToward>4 || snake.SnakeToward<1 {
		return errors.New("蛇的朝向异常")
	}
	gsMap.Canvas[snake.SnakeLine[0].Ordinate][snake.SnakeLine[0].Abscissa]=SnakeHeadIcons[snake.SnakeToward-1]
	return nil
}

func (gsMap *GSMap) GetBorder () (Border []Vector) {
	var border1 = Vector{
		Coordinate: Coordinate{0, 0},
		Direction:  Right,
	}
	var border2 = Vector{
		Coordinate: Coordinate{0, gsMap.MapLength - 1},
		Direction:  Down,
	}
	var border3 = Vector{
		Coordinate: Coordinate{gsMap.MapWidth - 1, gsMap.MapLength - 1},
		Direction:  Left,
	}
	var border4 = Vector{
		Coordinate: Coordinate{gsMap.MapWidth - 1, 0},
		Direction:  Up,
	}
	var border5 = Vector{
		Coordinate: Coordinate{0, 0},
		Direction:  End,
	}
	Border=[]Vector{border1,border2,border3,border4,border5}
	return Border
}

func (snake *Snake) Init (gsMap *GSMap,configuration string) error {
	if (gsMap.MapLength < 4) || (gsMap.MapWidth < 4) {
		return errors.New("地图尺寸不够")
	}
	if (configuration == "") || (configuration == "default") {
		snake.SnakeToward=Down
		snake.SnakeLine=make([]Vector,0)
		var snakeHead = Vector{
			Coordinate: Coordinate{Ordinate: (gsMap.MapWidth) / 2, Abscissa: (gsMap.MapLength) / 2},
			Direction: Up ,
		}
		var snakeTail = Vector{
			Coordinate: Coordinate{Ordinate: (gsMap.MapWidth) / 2 - 1, Abscissa: (gsMap.MapLength) / 2},
			Direction: End ,
		}
		snake.SnakeLine=append(snake.SnakeLine,snakeHead,snakeTail)
		return nil
	}
	return errors.New("蛇的参数不符合要求")
}

func (snake *Snake) SnakeHeadStretch (NextToward int){
	//下列关于的两个if语句实现蛇头伸长
	if NextToward==0 {
		if snake.SnakeToward == Up {
			snake.SnakeLine[0].Ordinate = snake.SnakeLine[0].Ordinate - 1
		}
		if snake.SnakeToward == Right {
			snake.SnakeLine[0].Abscissa = snake.SnakeLine[0].Abscissa + 1
		}
		if snake.SnakeToward == Down {
			snake.SnakeLine[0].Ordinate = snake.SnakeLine[0].Ordinate + 1
		}
		if snake.SnakeToward == Left {
			snake.SnakeLine[0].Abscissa = snake.SnakeLine[0].Abscissa - 1
		}
	}
	if (NextToward >0) && (NextToward <5) {
		var NewSnakeHead = Vector{Direction: Reverse(NextToward)}
		if NextToward == Up {
			NewSnakeHead.Ordinate = snake.SnakeLine[0].Ordinate - 1
			NewSnakeHead.Abscissa = snake.SnakeLine[0].Abscissa
		}
		if NextToward == Right {
			NewSnakeHead.Ordinate = snake.SnakeLine[0].Ordinate
			NewSnakeHead.Abscissa = snake.SnakeLine[0].Abscissa + 1
		}
		if NextToward == Down {
			NewSnakeHead.Ordinate = snake.SnakeLine[0].Ordinate + 1
			NewSnakeHead.Abscissa = snake.SnakeLine[0].Abscissa
		}
		if NextToward == Left {
			NewSnakeHead.Ordinate = snake.SnakeLine[0].Ordinate
			NewSnakeHead.Abscissa = snake.SnakeLine[0].Abscissa - 1
		}
		snake.SnakeLine = append([]Vector{NewSnakeHead}, snake.SnakeLine...)
		snake.SnakeToward = NextToward
	}
}

func (snake *Snake) IfSnakeCollide (gsMap *GSMap) (bool,error) {
	snakeHead:=snake.SnakeLine[0].Coordinate
	collideMap,err:=IfPointCollideVectorLine(snakeHead,gsMap.GetBorder())
	if err!=nil {
		return true,errors.New("IfSnakeCollide函数执行失败："+err.Error())
	}
	collideBody,err:=IfPointCollideVectorLine(snakeHead,snake.SnakeLine[1:])
	if err!=nil {
		return true,errors.New("IfSnakeCollide函数执行失败："+err.Error())
	}
	if collideMap==false && collideBody==false {
		return false,nil
	}
	return true,nil
}

func (snake *Snake) IfSnakeEatCandy (candy *Candy) bool {
	if snake.SnakeLine[0].Coordinate == candy.Coordinate {
		return true
	}
	return false
}

func (snake *Snake) RemoveTail () error {
	secondToLast:=len(snake.SnakeLine)-2
	lastPoint:=&snake.SnakeLine[len(snake.SnakeLine)-1]
	if secondToLast<0 {
		return errors.New("蛇身不符合规范")
	}
	direction:=snake.SnakeLine[secondToLast].Direction
	if direction == Up {
		lastPoint.Ordinate=lastPoint.Ordinate+1
	}
	if direction == Right {
		lastPoint.Abscissa=lastPoint.Abscissa-1
	}
	if direction == Down {
		lastPoint.Ordinate=lastPoint.Ordinate-1
	}
	if direction == Left {
		lastPoint.Abscissa=lastPoint.Abscissa+1
	}
	//倒数第二个点是否与最后一个点重合
	if snake.SnakeLine[secondToLast].Coordinate == lastPoint.Coordinate {
		snake.SnakeLine=snake.SnakeLine[:secondToLast+1]
		snake.SnakeLine[secondToLast].Direction=End
	}
	return nil
}

func (candy *Candy) NewCandyRandomly (gsMap *GSMap,snake *Snake) error {
	lengthLimitation:=gsMap.MapLength
	widthLimitation:=gsMap.MapWidth
	if (lengthLimitation<4)||(widthLimitation<4) {
		return errors.New("地图不符合要求，创建糖果失败")
	}
	rand.Seed(time.Now().Unix())
	//糖果是否与蛇碰撞
	for true {
		candy.Ordinate=rand.Intn(widthLimitation-2)+1
		candy.Abscissa=rand.Intn(lengthLimitation-2)+1
		collide,err:=IfPointCollideVectorLine(candy.Coordinate,snake.SnakeLine)
		if err!=nil {
			return errors.New("IfPointCollideLine函数运行失败："+err.Error())
		}
		if !collide {
			break
		}
	}
	return nil
}

func IfPointCollideVectorLine(point Coordinate,line []Vector) (bool,error) {
	if len(line)<1 {
		return true,errors.New("空线")
	}
	for i := 0; i < len(line)-1; i++ {
		if line[i].Direction==Up {
			if point.Abscissa == line[i].Abscissa {
				if (point.Ordinate > line[i+1].Ordinate-1) && (point.Ordinate < line[i].Ordinate+1) {
					return true,nil
				}
			}
		}
		if line[i].Direction==Right {
			if point.Ordinate == line[i].Ordinate {
				if (point.Abscissa > line[i].Abscissa-1) && (point.Abscissa < line[i+1].Abscissa+1) {
					return true,nil
				}
			}
		}
		if line[i].Direction==Down {
			if point.Abscissa == line[i].Abscissa {
				if (point.Ordinate > line[i].Ordinate-1) && (point.Ordinate < line[i+1].Ordinate+1) {
					return true,nil
				}
			}
		}
		if line[i].Direction==Left {
			if point.Ordinate == line[i].Ordinate {
				if (point.Abscissa > line[i+1].Abscissa-1) && (point.Abscissa < line[i].Abscissa+1) {
					return true,nil
				}
			}
		}
		if line[i].Direction==End {
			return false,errors.New("蛇末提前到来")
		}
	}
	return false,nil
}

func ClearToBlank (gsMap *GSMap) {
	for i:=1;i<gsMap.MapWidth-1;i++ {
		for j := 1; j < gsMap.MapLength-1; j++ {
			gsMap.Canvas[i][j]=Blank
		}
	}
}

func Reverse(direction int) (reverseDirection int) {
	return (direction+1)%4+1
}